#include "Weapon.h"
#include "Railgun.h"
#include "../Utils/Timer.h"
#include "../Utils/Util.h"
#include "../World.h"
#include "../Map.h"
#include "../Explosion.h"
#include "../Graphics/ParticleSystem/OneTimeLinearParticleEmitter.h"
#include "../Sound/Sound.h"

#include <cassert>
#include <GL/gl.h>
#include <iostream>


RailgunBolt::RailgunBolt(Ship *parent_ship, float speedm) : 
    parent_ship(parent_ship),
    is_active(true),
    life(1.0f) {

    assert(parent_ship != NULL);

    start_pos = parent_ship->getPosition();

    Vector2D fire_dir = parent_ship->getHeading();
    fire_dir.normalise();

    // Project fire dir to infinity to get a ray segment.
    Vector2D possible_end_pos = end_pos = start_pos + 100.0f*fire_dir;
    Vector2D tmp;
    World::instance().getMap()->collideRaySegment(start_pos, possible_end_pos, tmp, end_pos);

    Ship *collide_ship = findClosestShipCollision(start_pos, end_pos, start_pos, end_pos);
    
    if(collide_ship != NULL){
        collide_ship->dealDamage(110);
        collide_ship->impact(end_pos - collide_ship->getPosition());
    }

    orig_start_pos = start_pos;
    orig_end_pos = end_pos;

    perp = end_pos - start_pos;
    perp.normalise();
    Vector2D velocity_bias = perp;
    perp.rotate(90.0f);

    velocity_bias.scale(2.0f);
    float col_r = 0.3f, col_g = 0.6f, col_b = 1.0f;
    float duration = 2.0f;
    float size = 0.07; //0.016f;
    unsigned num_particles = (start_pos - end_pos).length() * 10;

    explosion_emitter = new OneTimeLinearParticleEmitter(num_particles, 
                                                         start_pos, end_pos, 
                                                         velocity_bias, 
                                                         0.75f, 0.75f,
                                                         col_r, col_g, col_b, 
                                                         1.0f, 0.1f, 
                                                         size, 0.0f, 
                                                         1.0f/duration, 0.2f/duration, 
                                                         0.0f, 0.0f,
                                                         "data/Cloud.png");

    explosion_psys = new ParticleSystem(explosion_emitter);
}

RailgunBolt::~RailgunBolt(){

}

void RailgunBolt::render(void){
    if(life > 0.0f){
    glPushMatrix();

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);

    float width = 0.02f;
    glBegin(GL_QUADS);
      glColor4f(0.0f, 0.0f, 0.0f, 0.5f); 
      glVertex3f(start_pos.x - perp.x*width, start_pos.y - perp.y*width, 0.0f);  
      glVertex3f(start_pos.x + perp.x*width, start_pos.y + perp.y*width, 0.0f);

      glColor4f(0.3f, 0.6f, 1.0f, 0.5f);
      glVertex3f(end_pos.x + perp.x*width, end_pos.y + perp.y*width, 0.0f);
      glVertex3f(end_pos.x - perp.x*width, end_pos.y - perp.y*width, 0.0f);
    glEnd();

    width = 0.01;
    glBegin(GL_QUADS);
      glColor4f(0.0f, 0.0f, 0.0f, 1.0f); 
      glVertex3f(start_pos.x - perp.x*width, start_pos.y - perp.y*width, 0.0f);  
      glVertex3f(start_pos.x + perp.x*width, start_pos.y + perp.y*width, 0.0f);

      glColor4f(0.3f, 0.6f, 1.0f, 1.0f);
      glVertex3f(end_pos.x + perp.x*width, end_pos.y + perp.y*width, 0.0f);
      glVertex3f(end_pos.x - perp.x*width, end_pos.y - perp.y*width, 0.0f);
    glEnd();

    glDisable(GL_BLEND);

    glPopMatrix();
    }

    explosion_psys->render();
}

void RailgunBolt::update(void){
    start_pos = orig_end_pos + life*(orig_start_pos - orig_end_pos);

    if(life > 0.0f){
        life -= (1.0f/0.66f)*Timer::secondsSinceLastFrame();
    }
    else{
        is_active = false; 
    }

    explosion_psys->update();
}

bool RailgunBolt::active(void){
    return is_active || explosion_psys->active();
}

bool RailgunBolt::resolveCollision(Ship *ship){    
    return false;
}

bool RailgunBolt::resolveCollision(Weapon *weapon){
    return false;
}

bool RailgunBolt::resolveCollision(Map *map){
    return false;
}

Ship* RailgunBolt::findClosestShipCollision(Vector2D start, Vector2D end, Vector2D &rstart, Vector2D &rend){
    Ship *result = NULL;
    float closest_dist = 0.0f;
    bool found_collision = false;

    rstart = start;
    rend = end;
    
    std::vector<WorldObject*> all_ships = World::instance().getAllObjectsOfType(SHIP_WOBJECT);
    for(unsigned i = 0; i < all_ships.size(); i++){
        Ship *cur_ship = (Ship* )(all_ships[i]);
        if(cur_ship == parent_ship){ continue; }        

        Vector2D cp;
        float dist = Util::pointSegmentDist(cur_ship->getPosition(), start, end, cp);
        if(dist < cur_ship->getSize()){
            float p = sqrtf(cur_ship->getSize()*cur_ship->getSize() - dist*dist);
            Vector2D dir = start-cp;
            dir.normalise();
            
            Vector2D ipoint = cp + p*dir;
            rstart = start;
            rend = ipoint;

            if(!found_collision || (ipoint-start).length() < closest_dist){
                closest_dist = (ipoint-start).length();
                found_collision = true;
                result = cur_ship;
            }
        }
    }

    return result;
}




Railgun::Railgun(int ammo_count, int lifetime, Ship *pship) :
    WeaponGenerator(ammo_count, lifetime, 1.0f, pship),
    bolt_sound(Sound::instance().load("data/Sounds/rail_gun.mp3")) {

    assert(parent_ship != NULL);
}

Railgun::~Railgun(){
    Sound::instance().unload(bolt_sound);
}

bool Railgun::shouldDiscard(void){
    // Has the weapon expired because it has been picked up too long ago?
    if(lifetime > 0 && (spawn_time + lifetime*1000) > Timer::currentTicks()){
        return true;
    }
    // Has the weapon expired because its ammo count is exhausted?
    else if(ammo_count == 0){
        return true;
    }
    else{
        return false;
    }
}

bool Railgun::fire(void) {
    // Figure out if we CAN fire first
    if(ammo_count != 0 && // have we run out of ammo?
       (Timer::currentTicks() - time_of_last_fire)/1000.0f >= rate_of_fire || time_of_last_fire == 0){ // did we fire too recently?
        
        time_of_last_fire = Timer::currentTicks();
        ammo_count--;

        World::instance().addObject(new RailgunBolt(parent_ship, 3.0f));

        Sound::instance().play(bolt_sound);
        return true;
    }
    else{
        return false;
    }
}

